package godbv2

import (
	"errors"
	"reflect"
	"strings"
)

type dbTag struct {
	PKName    string
	Fields    map[string]any
	TableName string
}

// parseObj 解析对象结构
// @param tp reflect.Value 对象
func parseObj(obj reflect.Value) (dbTag, error) {
	dbtag := dbTag{Fields: make(map[string]any)}
	tp := obj.Type()
	if tp.Kind() != reflect.Struct {
		return dbtag, errors.New("只能转换结构体")
	}
	x := tp.NumField()
	var field reflect.StructField
	var db string
	for i := 0; i < x; i++ {
		field = tp.Field(i)
		dbTags := strings.Split(field.Tag.Get("db"), ",")
		db = dbTags[0]
		if db == "" {
			db = field.Name
		}
		if db == "-" { //忽略
			continue
		}
		objField := obj.FieldByName(field.Name)
		if len(dbTags) == 2 && strings.HasPrefix(strings.ToLower(dbTags[1]), "null") {
			if objField.IsZero() {
				if strings.ToLower(dbTags[1]) == "null" {
					// insert的时候忽略该字段，update的时候设置成Null
					dbtag.Fields[field.Name] = nil
					continue
				} else if strings.ToLower(dbTags[1]) == "null2" {
					// 直接忽略这个字段， 作用是update的时候可以不更新这个字段，
					continue
				}
			}
		}
		if objField.Type().Kind() == reflect.Interface || objField.Type().Kind() == reflect.Pointer {
			dbtag.Fields[db] = objField.Elem().Interface()
		} else {
			dbtag.Fields[db] = objField.Interface()
		}
		if dbtag.TableName == "" {
			table := strings.TrimSpace(field.Tag.Get("table"))
			if table != "" {
				dbtag.TableName = table
				dbtag.PKName = db
			}
		}
	}
	return dbtag, nil
}

func checkObj(obj any) (dbTag, error) {
	if obj == nil {
		return dbTag{}, errors.New("对象信息空")
	}
	objVal := reflect.ValueOf(obj)
	dt, err := parseObj(objVal)
	if err != nil {
		return dt, err
	}
	if dt.TableName == "" {
		return dt, errors.New("表信息不存在")
	}
	return dt, nil
}

// InsertStruct 保存对象
func InsertStruct(obj any, db Query) error {
	dt, err := checkObj(obj)
	if err != nil {
		return err
	}
	sqlString := strings.Builder{}
	valString := strings.Builder{}
	var args []any
	sqlString.WriteString("INSERT INTO ")
	sqlString.WriteString(dt.TableName)
	sqlString.WriteString("(")
	valString.WriteString(") VALUES (")
	for k, v := range dt.Fields {
		if v == nil {
			continue
		}
		sqlString.WriteString(k + ",")
		valString.WriteString("?,")
		args = append(args, v)
	}
	startSQL := strings.TrimSuffix(sqlString.String(), ",")
	endString := strings.TrimSuffix(valString.String(), ",")
	return db.Exec(startSQL+endString+")", args...).Error(true)
}

// UpdateStruct 更新对象
func UpdateStruct(obj any, db Query) error {
	dt, err := checkObj(obj)
	if err != nil {
		return err
	}
	sqlBuilder := strings.Builder{}
	sqlBuilder.WriteString("UPDATE ")
	sqlBuilder.WriteString(dt.TableName)
	sqlBuilder.WriteString(" SET ")
	var args []any
	for k, v := range dt.Fields {
		if k == dt.PKName {
			continue
		}
		if v == nil {
			sqlBuilder.WriteString(k + " = NULL,")
			continue
		}
		sqlBuilder.WriteString(k + "=?,")
		args = append(args, v)
	}
	sqlString := strings.TrimSuffix(sqlBuilder.String(), ",")
	sqlString = sqlString + " WHERE " + dt.PKName + " = ? "
	args = append(args, dt.Fields[dt.PKName])
	return db.Exec(sqlString, args...).Error()
}
